const CFG = require('../config');
const rCache = require('../model/redis_cache');
const dbPcyData = require('../model/model_data');
const timLib = require('../comm/time-lib');
const FUN = require('../comm/func');

//计算结果算式
function calcValueRes(dConf,value)
{
  if(dConf.res_calc)//执行结果算式
  {
    let calcStr=dConf.res_calc.replace(/#v/g,value);
    try {
      return value=eval(calcStr);//通过算式计算数值
    } catch(err) {
      console.log('[%d]eval calc res error![',dConf.policy_id,calcStr,']',err);
    }
  }
  else
  {
    return Number(value);
  }
}

//输出数据到数据库
// dConf 配置项
// save 对象数组
// addr 报文地址
// stamp 存储时间戳
// saveCallback 存储回调
function saveOutReg(dConf,save,addr,stamp,saveCallback=null)
{
  let sum=0,cnt=0;
  let max=-9999999999;
  let min=9999999999;
  let jsonObj={};
  let blist=dConf.blist;//黑名单

  if(dConf.stamp_offset) stamp+=Number(dConf.stamp_offset);//时间戳存储偏移

  //黑名单模式 #1,#1:3
  if(blist) blist=blist.split(",");
  if(!(blist && blist.includes('#'+addr))){
    save.some(i=>{
      if(!(blist && blist.includes(i.name))){
        i.value=calcValueRes(dConf,i.value);
        jsonObj[i.reg]=i.value;

        if(dConf.excep)//剔除值的处理
        {
          let excep=dConf.excep.split(',');
          if(!excep.includes(i.value.toString()))
          {
            sum+=i.value;//累加和
            if(i.value>max) max=i.value;//找最大值
            if(i.value<min) min=i.value;//找最小值
            cnt++;
          }
        } else {//不需要处理剔除值
          sum+=i.value;//累加和
          if(i.value>max) max=i.value;//找最大值
          if(i.value<min) min=i.value;//找最小值
          cnt++;
        }

        if(saveCallback) saveCallback('reg','hwid,policy_id,addr,reg,value,stamp',[dConf.hwid,dConf.policy_id,addr,i.reg,i.value,stamp]);//记录到数据库
      }
    })//end some
  }

  if(cnt)//集中存储
  {
    jsonObj['max']=Number(max).toFixed(2);
    jsonObj['min']=Number(min).toFixed(2);
    jsonObj['ave']=Number(sum/cnt).toFixed(2);//求平均值，保留2位小数
    if(saveCallback) saveCallback('reg-json','hwid,policy_id,addr,json,stamp',[dConf.hwid,dConf.policy_id,addr,JSON.stringify(jsonObj),stamp]);
  }
}

//根据模板输出变量
// dConf 配置项
// save 对象数组
// addr 报文地址
// stamp 存储时间戳
// saveCallback 存储回调
function saveOutVar(dConf,save,addr,stamp,saveCallback)
{
  if(dConf.stamp_offset) stamp+=Number(dConf.stamp_offset);//时间戳存储偏移

  //输出变量
  if(dConf.out && dConf.calc){
    let out=dConf.out.split(",");//输出变量的名称
    let calc=dConf.calc.split(",");//算式
    let jsonVarsObj={};

    for(let i in out){
      if(calc[i]==null) break;
      let calcStr=calc[i];
      let varName=out[i];

      //console.log('[%d]calc',dConf.policy_id,varName,calcStr);
      let regs=calcStr.match(/#[0-9]+:[0-9]+/g);//找到类似#3:1这样的寄存器标号
      if(regs)
      for(let regId of regs){
        let findVal = save.find(i=>{return i.name==regId});
        if(findVal){
          let regStr=new RegExp(regId,'g');
          calcStr=calcStr.replace(regStr,findVal.value);
        }
      }
      if(calcStr.match(/#[0-9]+:[0-9]+/g) == null)//寄存器值已经全部替换
      {
        try {
          let calcVal=eval(calcStr);//通过算式计算数值
          jsonVarsObj[varName]=Number(calcVal).toFixed(2);
          if(saveCallback)
            saveCallback('var','hwid,policy_id,vtag,value,stamp',[dConf.hwid,dConf.policy_id,varName,Number(calcVal).toFixed(2),stamp]);//记录到数据库
        } catch(err) {
          console.log('[%d]eval calc string error![',dConf.policy_id,calcStr,']',err);
        }
      }
    }//end for in out

    //集中存储
    if(Object.keys(jsonVarsObj).length)
    {
      //jsonVarsObj['ave']=Number(Number(sum/Object.keys(v).length).toFixed(2));//求平均值，保留2位小数
      if(saveCallback)
        saveCallback('var-json','hwid,policy_id,addr,json,stamp',[dConf.hwid,dConf.policy_id,addr,JSON.stringify(jsonVarsObj),stamp]);
    }
  }//end 输出变量
}

//根据缓存计算前一个时间点的平均值
//根据缓存内容取平均值
//会处理策略下的所有地址报文
//传入的devConf必须是从站配置
//nodeDev:redis源数据缓存节点
//dConf:策略参数
//mode:ave,max,min
async function prevDataCalc(nodeDev,dConf,mode='ave',saveCallback=null)
{
  let stamp=timLib.NowStamp();//防止访问数据库太过频繁
  let preStamp=timLib.PrevRounding(dConf.fetch_period);//获取前一段的时间戳
  let nodeStamp=nodeDev+':'+preStamp;

  let db_stamp=await rCache.getKey(nodeDev,'db_stamp');
  if(db_stamp!=null && stamp-db_stamp<CFG.DATA_SAVE_GAP)//存储到数据库的频率不能过快
  {console.log('[%d] mdata fast(%dmS)',dConf.hwid,stamp-db_stamp,timLib.NowStr());return;}

  let addrs=await rCache.getAll(nodeStamp,'addrs');
  for(let addr in addrs)//每个地址的都找出来
  {
    let nodeStampAddr=nodeStamp+':'+addr;
    let count=await rCache.getKey(nodeStampAddr,'count');
    if(count==null || count==0) return;//数据已经处理过了

    let nodes=await rCache.keys(nodeStampAddr,'reg.*');
    if(nodes==null){console.log('[%d] not cache record',dConf.hwid);return;}

    let save=[];
    for(let i=0;i<nodes.length;i++) //轮询每个寄存器节点，求这段时间的平均值
    {
      let regId=nodes[i].match(/[0-9]{1,}$/g);//获取寄存器号
      let valsObj=await rCache.fetchAll(nodes[i]);//获取寄存器上报的所有批次数据

      if(valsObj==null){console.error('[%d] not cache val array',dConf.policy_id);return;}//无上报数据
      //if(count!=valArray.length){console.error('[%d] reg val num err',dConf.policy_id,count,valArray.length);continue;}//上报次数不对

      let saveRegVal=0;
      if(mode=='ave') saveRegVal=FUN.arrAverage(valsObj);//求平均值
      else if(mode=='max') saveRegVal=FUN.arrMaxValue(valsObj);//求最大值
      else if(mode=='min') saveRegVal=FUN.arrMinValue(valsObj);//求最小值

      save.push({reg:regId[0],value:saveRegVal,name:'#'+addr+':'+regId[0]});//加入到数组
    }

    //输出到数据库
    saveOutReg(dConf,save,addr,preStamp,saveCallback);
    saveOutVar(dConf,save,addr,preStamp,saveCallback);
    rCache.setKey(nodeDev,'db_stamp',stamp,60);//记录数据库访问时间戳
    console.log('[%d][%d] '+mode+' addr%d',dConf.policy_id,dConf.hwid,addr,preStamp,timLib.Stamp2Str(preStamp));

    //删除缓存中的使用过的数据
    rCache.del(nodeStamp,'addrs');
  }//end let addr

  //删除数据库中冗余的前期数据
  dbRecordTidy(dConf.uid,dConf.table,dConf.policy_id,dConf.record_limit,dConf.record_duration);
  if(dConf.out && dConf.calc && dConf.out_tab)
    dbRecordTidy(dConf.uid,dConf.out_tab,dConf.policy_id,dConf.record_limit,dConf.record_duration);
}

//增量计算
async function prevDataCalcInc(nodeDev,dConf,addr,saveCallback=null)
{
  let stamp=timLib.NowStamp();//防止访问数据库太过频繁
  let preStamp=timLib.PrevRounding(dConf.fetch_period);//获取前一段的时间戳
  let nowStamp=timLib.Rounding(dConf.fetch_period);//获取当前的时间戳
  let pre_NodeStamp=nodeDev+':'+preStamp;
  let now_NodeStamp=nodeDev+':'+nowStamp;
  let pre_NodeStampAddr=pre_NodeStamp+':'+addr;
  let now_NodeStampAddr=now_NodeStamp+':'+addr;

  let db_stamp=await rCache.getKey(nodeDev,'db_stamp');
  if(db_stamp!=null && stamp-db_stamp<CFG.DATA_SAVE_GAP)//存储到数据库的频率不能过快
  {console.log('[%d] mdata fast(%dmS)',dConf.hwid,stamp-db_stamp,timLib.NowStr());return;}

  if(Number(addr))
  {
    //获取前一个时间段的数据
    let nodes1=await rCache.keys(pre_NodeStampAddr,'reg.*');
    if(nodes1==null){console.log('[%d] not cache1 record',dConf.hwid);return;}

    let oldData=[];
    for(let i=0;i<nodes1.length;i++) //轮询每个寄存器节点
    {
      let regId=nodes1[i].match(/[0-9]{1,}$/g);//获取寄存器号
      let valsObj=await rCache.fetchAll(nodes1[i]);//获取寄存器上报的所有批次数据

      if(valsObj==null){console.error('[%d] not cache val array',dConf.policy_id);return;}//无上报数据

      if(valsObj.length==1)
        oldData.push({reg:Number(regId[0]),value:valsObj[0],name:'#'+addr+':'+regId[0]});//加入到数组
    }

    //获取后一个时间段数据
    let nodes2=await rCache.keys(now_NodeStampAddr,'reg.*');
    if(nodes2==null){console.log('[%d] not cache2 record',dConf.hwid);return;}

    let newData=[];
    for(let i=0;i<nodes2.length;i++) //轮询每个寄存器节点
    {
      let regId=nodes2[i].match(/[0-9]{1,}$/g);//获取寄存器号
      let valsObj=await rCache.fetchAll(nodes2[i]);//获取寄存器上报的所有批次数据

      if(valsObj==null){console.error('[%d] not cache val array',dConf.policy_id);return;}//无上报数据

      if(valsObj.length==1)
      newData.push({reg:Number(regId[0]),value:valsObj[0],name:'#'+addr+':'+regId[0]});//加入到数组
    }
    //console.log('inc old',pre_NodeStampAddr,oldData);
    //console.log('inc new',now_NodeStampAddr,newData);

    if(oldData.length!=newData.length){
      console.log('[%d][%d] waitting data...',dConf.policy_id,dConf.hwid);
      return;
    }

    let save=[];
    newData.some((newOne)=>{
      let old=oldData.find(a=>{return a.reg==newOne.reg});
      if(old)
      {
        let gap=Number(Number(newOne.value)-Number(old.value)).toFixed(4);
        //console.log('--',newOne.reg,gap);
        newOne.value=gap;
        save.push(newOne);//加入到数组
      }
    })

    //输出到数据库
    saveOutVar(dConf,save,addr,nowStamp-1000,saveCallback);
    rCache.setKey(nodeDev,'db_stamp',stamp,60);//记录数据库访问时间戳
    console.log('[%d][%d] inc addr%d',dConf.policy_id,dConf.hwid,addr,stamp,timLib.Stamp2Str(stamp));
  }//end let addr

  //删除数据库中冗余的前期数据
  dbRecordTidy(dConf.uid,dConf.table,dConf.policy_id,dConf.record_limit,dConf.record_duration);
  if(dConf.out && dConf.calc && dConf.out_tab)
    dbRecordTidy(dConf.uid,dConf.out_tab,dConf.policy_id,dConf.record_limit,dConf.record_duration);
}

//整理数据库过时数据
function dbRecordTidy(uid,table,policy_id,record_limit,record_duration=0)
{
  if(!table) return;

  if(record_limit!=null && record_limit!=0) //如果limit有值，有限考虑limit
  {
    dbPcyData.cleanByLimit(uid,table,policy_id,record_limit);
  }
  else if(record_duration!=null && record_duration!=0) //limit无值，以时间段为主
  {
    let stamp=timLib.NowStamp()-record_duration*1000;
    dbPcyData.cleanByStamp(uid,table,policy_id,stamp);
  }
  else //存储不允许不做限制
  {
    dbPcyData.cleanByLimit(uid,table,policy_id,100);//保留100条
  }
}

exports.saveOutVar = saveOutVar;
exports.saveOutReg = saveOutReg;
exports.prevDataCalc = prevDataCalc;
exports.prevDataCalcInc = prevDataCalcInc;
exports.dbRecordTidy = dbRecordTidy;
